package com.github.icloudpay.pay.core.service.pay.wechat.service;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.lang.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.github.icloudpay.pay.core.feign.PayChannelConfigFeign;
import com.github.icloudpay.pay.core.inter.PayObjectService;
import com.github.icloudpay.pay.core.service.pay.wechat.domain.RequestElement;
import com.github.icloudpay.pay.core.service.pay.wechat.util.WechatPayConfig;
import com.github.wxiaoqi.security.common.admin.pay.request.OnlinePaymentRequest;
import com.github.wxiaoqi.security.common.msg.ResponseCode;
import com.github.wxiaoqi.security.common.util.Utils;

/**
 * 微信公众号支付
 * @author hexufeng
 *
 */
@Service("wechatJsApiPaymentService")
public class WechatJsApiPaymentService implements PayObjectService<OnlinePaymentRequest> {

	private static final Logger logger = LoggerFactory.getLogger(WechatJsApiPaymentService.class);

	@Autowired
	private PayChannelConfigFeign payChannelConfigFeign;

	@Autowired
	private WechatSignatureService wechatSignatureService;

	@Autowired
	private WechatPayConfig wechatPayConfig;

	public Map<String, Object> pay(OnlinePaymentRequest request) {
		
		logger.info("****************调用微信公众号支付服务接口开始*******************");
		
		Map<String, Object> responseMap = new HashMap<String, Object>();

		Map<String, Object> reqMap = new HashMap<String, Object>();
    	reqMap.put("platformId", request.getPlatformId());
    	reqMap.put("merchantId", request.getMerchantId());
    	reqMap.put("payChannelNo", request.getPayChannelNo());
		Map<String, Object> respMap = payChannelConfigFeign.query(reqMap);
        if(!(boolean) respMap.get("success")){
			responseMap.put("success", false);
			responseMap.put("code", ResponseCode.PAYCHANNEL_CONFIGURATION_NOTEXIST.getCode());
			responseMap.put("msg", ResponseCode.PAYCHANNEL_CONFIGURATION_NOTEXIST.getMessage());
			return responseMap;
		}
        
        @SuppressWarnings("unchecked")
		Map<String, Object> configMap = (Map<String, Object>)respMap.get("payChannelConfigInfo");
        
		logger.info("支付通道配置信息：{}",configMap);
		String notifyUrl = wechatPayConfig.getBackCallbackUrl() + "/callBack/wechatPay";;

		String nonce_str = ObjectUtils.toString((new Random().nextInt() * (99999 - 10000 + 1)) + 10000);

		// 根据获取的参数拼装XML信息
		RequestElement reqElement = new RequestElement();
		reqElement.setAppid((String)configMap.get("outMerAccount"));
		reqElement.setBody(request.getProductName());
		reqElement.setMch_id((String)configMap.get("outMerNo"));
		if (!StringUtils.isEmpty((String)configMap.get("outSubMerNo"))) {
		    reqElement.setSub_mch_id((String)configMap.get("outSubMerNo"));
        }
		reqElement.setNonce_str(nonce_str);
		reqElement.setNotify_url(notifyUrl);
		reqElement.setOut_trade_no(request.getSerialNo());
		reqElement.setSpbill_create_ip(request.getExterInvokeIp());
		reqElement.setTotal_fee(Utils.changeToDivide(request.getOrderAmt().toString()));
		reqElement.setTrade_type("JSAPI");
		reqElement.setOpenid((String) request.getExtra().get("openId"));

		// 获取签名信息

		try {
			reqElement.setSign(wechatSignatureService.sign(reqElement, configMap));
		} catch (Exception e1) {
			e1.printStackTrace();
		}

		String xmlStr = null;
		try {
			xmlStr = reqData2Xml(reqElement);
		} catch (JAXBException e) {
			logger.error("解析微信返回数据失败", e);
			responseMap.put("success", false);
			responseMap.put("code", ResponseCode.WECHAT_RESOLVE_FAIL.getCode());
			responseMap.put("msg", ResponseCode.WECHAT_RESOLVE_FAIL.getMessage());
			return responseMap;
		}

		// 调用微信统一下单接口
//		String payUrl = wechatPayConfig.getWechatGatewayUrl();
		String payUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
		HttpClient client = new HttpClient();
		PostMethod post = new PostMethod(payUrl);
		Map<String, Object> resultMap = new HashMap<String, Object>();
		try {
			RequestEntity entity = new StringRequestEntity(xmlStr, "text/xml", "UTF-8");
			post.setRequestEntity(entity);
			client.executeMethod(post);
			int code = post.getStatusCode();
			if (code == HttpStatus.SC_OK) {
				String info = new String(post.getResponseBody(), "UTF-8");
				resultMap = receiveResult(info, configMap, request.getSerialNo());
				logger.info("发送成功，返回的结果：", resultMap);
				if("SUCCESS".equals(resultMap.get("return_code"))){
					responseMap.put("payInfo", resultMap);
					responseMap.put("success", true);
					responseMap.put("code", ResponseCode.OK.getCode());
					responseMap.put("msg", ResponseCode.OK.getMessage());
				}else{
					responseMap.put("success", false);
					responseMap.put("code", ResponseCode.PAY_FAIL.getCode());
					responseMap.put("msg", ResponseCode.PAY_FAIL.getMessage());
				}
			} else {
				logger.error("发送数据失败");
				responseMap.put("success", false);
				responseMap.put("code", ResponseCode.SEND_DATA_FAIL.getCode());
				responseMap.put("msg", ResponseCode.SEND_DATA_FAIL.getMessage());
			}

		} catch (Exception ex) {
			logger.error("返回失败", ex);
		} finally {
			post.releaseConnection();
		}
		return responseMap;

	}

	/**
	 * 将请球参数转为Xml格式
	 * 
	 * @throws JAXBException
	 * @throws ParserConfigurationException
	 */
	private String reqData2Xml(RequestElement reqElement) throws JAXBException {
		JAXBContext context = JAXBContext.newInstance(RequestElement.class);

		Marshaller marshaller = context.createMarshaller();
		marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");// 编码格式
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);// 是否格式化生成的xml串
		marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false);// 是否省略xml头信息（<?xml
																// version="1.0"
																// encoding="gb2312"
																// standalone="yes"?>）

		ByteArrayOutputStream outputstream = new ByteArrayOutputStream();

		StreamResult result = new StreamResult(outputstream);

		marshaller.marshal(reqElement, result);

		byte[] body = outputstream.toByteArray();

		return new String(body, Charset.forName("UTF-8"));
	}

	/**
	 * 解析获取的返回结果
	 * 
	 * @param info
	 * @return
	 * @throws ParserConfigurationException
	 * @throws IOException
	 * @throws SAXException
	 */
	private Map<String, Object> receiveResult(String info, Map<String, Object> configMap, String merOrderId) throws ParserConfigurationException, SAXException, IOException {

		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		InputStream is = getStringStream(info);
		Document document = builder.parse(is);

		// 获取到document里面的全部结点
		NodeList allNodes = document.getFirstChild().getChildNodes();
		Node node;
		Map<String, Object> paramMap = new HashMap<String, Object>();
		int i = 0;
		while (i < allNodes.getLength()) {
			node = allNodes.item(i);
			if (node instanceof Element) {
				paramMap.put(node.getNodeName(), node.getTextContent());
			}
			i++;
		}
		// Date dt=new Date();
		String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
		Random random = new Random();
		String nonceStr = ObjectUtils.toString((random.nextInt() * (99999 - 10000 + 1)) + 10000);
		// 生成jssdk支付签名
		Map<String, Object> signMap = new HashMap<String, Object>();
		signMap.put("appId", paramMap.get("appid"));
		signMap.put("nonceStr", nonceStr);
		signMap.put("package", "prepay_id=" + paramMap.get("prepay_id"));
		signMap.put("signType", "MD5");
		signMap.put("timeStamp", timeStamp);
		// 返回
		Map<String, Object> resultMap = new HashMap<String, Object>();
		resultMap.put("sign", wechatSignatureService.toSign(signMap, configMap));
		resultMap.put("appid", paramMap.get("appid"));
		resultMap.put("noncestr", nonceStr);
		resultMap.put("package", "prepay_id=" + paramMap.get("prepay_id"));
		resultMap.put("timestamp", timeStamp);
		resultMap.put("prepayid", paramMap.get("prepay_id"));
		resultMap.put("partnerid", paramMap.get("mch_id"));
		resultMap.put("return_code", paramMap.get("return_code"));
		resultMap.put("return_msg", paramMap.get("return_msg"));
		resultMap.put("merOrderId", merOrderId);
		return resultMap;
	}

	private InputStream getStringStream(String sInputString) throws UnsupportedEncodingException {
		ByteArrayInputStream tInputStringStream = null;
		if (sInputString != null && !sInputString.trim().equals("")) {
			tInputStringStream = new ByteArrayInputStream(sInputString.getBytes("UTF-8"));
		}
		return tInputStringStream;
	}

}
